package org.iati.mobile;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.iati.mobile.dataexchange.NetworkUtilities;
import org.iati.mobile.exception.IATIBadFilterException;
import org.iati.mobile.exception.IATIParseXMLException;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

import android.content.Context;
import android.util.Log;

/**
 * @author Ionut Dobre
 * @date April 9, 2012
 */

// TODO implement all methods with (start, end) parameters and maybe with caching...

public class IATIImpl implements IATI {
	private String serverURL;
	private String serverPort;
	private Context mContext;

	public IATIImpl (String serverURL, String serverPort) {
		this.serverURL = serverURL;
		this.serverPort = serverPort;
	}

	public IATIImpl (String serverURL, String serverPort ,Context mContext) {
		this.serverURL = serverURL;
		this.serverPort = serverPort;
		this.mContext = mContext;
	}

	@Override
	public Map<String, String> getAllDonors() 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/list/~participating-org/@role/Funding/format/xml/start/1/howmany/1000";

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(BASE_URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> allDonors = mResults.getItems();
			Map <String, String> donors = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < allDonors.size(); i++) {
				donors.put(allDonors.get(i).getId(), allDonors.get(i).getLabel());
			}

			return donors;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	@Override
	public Map<String, String> getProjectsByDonor(String donorId) 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/activities/participating-org~@ref/";
		final String END_URL = "/format/xml/start/1/howmany/1000";
		final String PARAMETERS_URL = donorId;

		String URL = BASE_URL + URLEncoder.encode(PARAMETERS_URL, "UTF-8") + END_URL;

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> projects = mResults.getItems();
			Map <String, String> projectsByDonor = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < projects.size(); i++) {
				projectsByDonor.put(projects.get(i).getId(), projects.get(i).getLabel());
			}

			return projectsByDonor;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	@Override
	public Map<String, String> getAllSectors() 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/list/~sector/format/xml/start/1/howmany/1000";

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(BASE_URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> allSectors = mResults.getItems();
			Map <String, String> sectors = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < allSectors.size(); i++) {
				sectors.put(allSectors.get(i).getId(), allSectors.get(i).getLabel());
			}

			return sectors;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	@Override
	public Map<String, String> getProjectsBySector(String sectorId) 
			throws IATIParseXMLException, IOException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/activities/sector~@code/";
		final String END_URL = "/format/xml/start/1/howmany/1000";
		final String PARAMETERS_URL = sectorId;

		String URL = BASE_URL + URLEncoder.encode(PARAMETERS_URL, "UTF-8") + END_URL;

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> projects = mResults.getItems();
			Map <String, String> projectsBySector = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < projects.size(); i++) {
				projectsBySector.put(projects.get(i).getId(), projects.get(i).getLabel());
			}

			return projectsBySector;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	@Override
	public Map<String, String> getAllRecipientCountries() 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/list/~recipient-country/format/xml/start/1/howmany/1000";

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(BASE_URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> allCountries = mResults.getItems();
			Map <String, String> recipientCountries = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < allCountries.size(); i++) {
				recipientCountries.put(allCountries.get(i).getId(), allCountries.get(i).getLabel());
			}

			return recipientCountries;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	@Override
	public Map<String, String> getProjectsByRecipientCountry(String recipientId) 
			throws IOException, IATIParseXMLException {		
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/activities/recipient-country~@code/";
		final String END_URL = "/format/xml/start/1/howmany/1000";
		final String PARAMETERS_URL = recipientId;

		String URL = BASE_URL + URLEncoder.encode(PARAMETERS_URL, "UTF-8") + END_URL;

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> projects = mResults.getItems();
			Map <String, String> projectsByRecipientCountry = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < projects.size(); i++) {
				projectsByRecipientCountry.put(projects.get(i).getId(), projects.get(i).getLabel());
			}

			return projectsByRecipientCountry;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	@Override
	public Map<String, String> getAllReportingOrganizations() 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/list/~reporting-org/format/xml/start/1/howmany/1000";

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(BASE_URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> allOrganizations = mResults.getItems();
			Map <String, String> reportingOrganizations = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < allOrganizations.size(); i++) {
				reportingOrganizations.put(allOrganizations.get(i).getId(), allOrganizations.get(i).getLabel());
			}

			return reportingOrganizations;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	@Override
	public Map<String, String> getProjectsByReportingOrganization(String reportingOrgId) 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/activities/reporting-org~@ref/";
		final String END_URL = "/format/xml/start/1/howmany/1000";
		final String PARAMETERS_URL = reportingOrgId;

		String URL = BASE_URL + URLEncoder.encode(PARAMETERS_URL, "UTF-8") + END_URL;

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> projects = mResults.getItems();
			Map <String, String> projectsByReportingOrganization = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < projects.size(); i++) {
				projectsByReportingOrganization.put(projects.get(i).getId(), projects.get(i).getLabel());
			}

			return projectsByReportingOrganization;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}
	
	@Override
	public Map<String, String> getProjectsByFilter(Filter filter)
			throws IOException, IATIParseXMLException, IATIBadFilterException {
		final String BASE_URL 	= serverURL + ":" + serverPort + "/agg/activities";
		final String END_URL 	= "/format/xml/start/1/howmany/1000";
		String criteria			= filter.generate();
		if ( criteria == null || criteria.length() == 0 )
			throw new IATIBadFilterException("Filter is empty !");
		
		String URL = BASE_URL + criteria + END_URL;
		
		
		return fetchActivities(URL);
		
	}

	@Override
	public IATIProject getProjectByIatiIdentifier(String id) 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/entity/~/filter/iati-identifier/";
		final String END_URL = "/format/xml";
		final String PARAMETERS_URL = id.replaceAll("/", "|");
		String URL = BASE_URL + URLEncoder.encode(PARAMETERS_URL, "UTF-8") + END_URL;

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(URL);
		IATIProject mIATIProject = new IATIProject ();

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			QueryResults mQueryResults = serial.read(QueryResults.class, responseFromServer);
			mIATIProject = mQueryResults.getmIATIProject();
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}

		return mIATIProject;
	}

	@Override
	public long getNumberOfProjects() 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/activities/format/xml/start/1/howmany/1";

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(BASE_URL);
		responseFromServer = responseFromServer.replaceAll("exist:hits", "hits").
				replaceAll("exist:start", "start").replaceAll("exist:count", "count");

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			return mResults.getExistHits();
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	@Override
	public Map<String, String> getAllProjects(long start, long end) 
			throws IOException, IATIParseXMLException {
		/**
		 * The Base URL - used to fetch the data
		 */
		final String BASE_URL = serverURL + ":" + serverPort + "/agg/activities/format/xml/start/1/howmany/1000";

		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(BASE_URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> allProjects = mResults.getItems();
			Map <String, String> projects = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < allProjects.size(); i++) {
				projects.put(allProjects.get(i).getId(), allProjects.get(i).getLabel());
			}

			return projects;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	/*
	 * write to cache
	 */
	private void writeCache (String fileName, String data) {
		Log.d("debug", "start write cache");

		try {
			// use Android cache folder
			Log.d ("debug", "cache dir: " + mContext.getCacheDir());
			FileOutputStream mFileOutputStream = new FileOutputStream(new File(mContext.getCacheDir(), fileName));

			OutputStreamWriter mOutputStreamWriter = new OutputStreamWriter(mFileOutputStream); 

			// Write the data to the file
			mOutputStreamWriter.write(data);

			/* ensure that everything is really written out and close */
			mOutputStreamWriter.flush();
			mOutputStreamWriter.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * read from cache	
	 */
	private String readCache (String fileName) {
		try {
			File mFile = new File(mContext.getCacheDir(), fileName);

			if (mFile.exists()) {
				Log.d("debug", "fisierul exista!");
			} else {
				Log.d("debug", "fisierul nu exista!");
			}

			InputStreamReader mInputStreamReader = new InputStreamReader(new FileInputStream(mFile));
			BufferedReader mBufferedReader = new BufferedReader(mInputStreamReader);

			String str="";

			/* Prepare a StringBuffer that will hold the chars we read */
			StringBuffer buf = new StringBuffer();

			// Fill the Buffer with data from the file
			if (mInputStreamReader != null) {							
				while ((str = mBufferedReader.readLine()) != null) {	
					buf.append(str + "\n" );
				}				
			}
			// Transform the chars to a String
			String readString = buf.toString();

			Log.d("debug", readString);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		Log.d("debug", "stop read");

		File dir = mContext.getFilesDir();
		File[] filelist = dir.listFiles();

		for (int i = 0; i < filelist.length; i++) {
			Log.d ("debug", "file: " + filelist[i].toString());
		}

		return null;
	}
	
	private Map<String, String> fetchActivities(String URL) throws IOException, IATIParseXMLException {
		// fetch the XML from the server
		String responseFromServer = NetworkUtilities.fetchFromServer(URL);

		// parse the XML received from the server
		Serializer serial = new Persister();
		try {
			Results mResults = serial.read(Results.class, responseFromServer);

			List<Item> projects = mResults.getItems();
			Map <String, String> projectsByReportingOrganization = new LinkedHashMap<String, String>();

			for(int i = 0 ; i < projects.size(); i++) {
				projectsByReportingOrganization.put(projects.get(i).getId(), projects.get(i).getLabel());
			}

			return projectsByReportingOrganization;
		} catch (Exception e) {
			throw new IATIParseXMLException();
		}
	}

	/**
	 * @return the serverURL
	 */
	public String getServerURL() {
		return serverURL;
	}

	/**
	 * @param serverURL the serverURL to set
	 */
	public void setServerURL(String serverURL) {
		this.serverURL = serverURL;
	}

	/**
	 * @return the serverPort
	 */
	public String getServerPort() {
		return serverPort;
	}

	/**
	 * @param serverPort the serverPort to set
	 */
	public void setServerPort(String serverPort) {
		this.serverPort = serverPort;
	}

	/**
	 * @return the mContext
	 */
	public Context getmContext() {
		return mContext;
	}

	/**
	 * @param mContext the mContext to set
	 */
	public void setmContext(Context mContext) {
		this.mContext = mContext;
	}

}
